module alu (
        //  input   
        op1,op2,aluctr,
        //  output  
        out,carry_out,zero
        );

//  aluctr      operation   
//  (arithmetic)
//  0000        add     op2+op1
//  0001        sub     op2-op1 
//  0010        xor     op2^op1
//  0011        andn    op1&~op2
//  (shift)
//  0100        rol
//  0101        sll
//  0110        ror
//  0111        srl
//  (set value)
//  1100        seq     1 if op1==op2
//  1101        slt     ~out[15] (op2-op1)
//  1110        sle     zero | ~out[15] (op2-op1)
//  1111        sco     carry_out   (op1+op2)
//  1000        btr
//  1001        slbi    Rd <- (Rs<<8) | I(zero.ext)

    input   [15:0]  op1,op2;    //  two operands of alu
    input   [3:0]   aluctr;

    output  [15:0]  out;
    output          carry_out,zero;

    wire    [1:0]   sh_mode;
    wire    [15:0]  shifter_out;
    shifter sh0 (
            .In(op1),
            .Cnt(op2[3:0]),
            .Op(aluctr[1:0]),
            .Out(shifter_out)
            );

    wire    [15:0]  sum,adder_out;
    wire            Cout, low;
    assign  low = 0;
    wire            add_sub;    //  0 for add; 1 for sub;
    assign          add_sub = aluctr[3]?(~(aluctr[1]&aluctr[0])):aluctr[0];

    cla16   add0    (
            .A(op1^{16{add_sub}}),
            .B(op2),
            .Cin(add_sub),
            .S(sum),
            .Cout(Cout),
            .zero(zero)
            );
    wire    seq;
    assign  seq = (op1==op2)?1'b1:1'b0;
    assign          adder_out = (aluctr == 4'b1100)?{{15{1'b0}},seq}
                            :   (aluctr == 4'b1101)?{{15{1'b0}},~Cout&~sum[15]}
                            :   (aluctr == 4'b1110)?{{15{1'b0}},seq|(~Cout&~sum[15])}
                            :   (aluctr == 4'b1111)?{{15{1'b0}},Cout}:sum;

    assign  carry_out = Cout^add_sub;

    wire    [15:0]  rev_op1;
    assign  rev_op1 = {
        op1[0],op1[1],op1[2],op1[3],
        op1[4],op1[5],op1[6],op1[7],
        op1[8],op1[9],op1[10],op1[11],
        op1[12],op1[13],op1[14],op1[15]
    };

    assign  out =   (aluctr == 4'b1000) ? rev_op1 
                :   (aluctr == 4'b1001) ? {op1[7:0],op2[7:0]}   //slbi
                :   (aluctr[3:2] == 2'b01) ? shifter_out 
                :   (aluctr == 4'b0010) ? op1^op2
                :   (aluctr == 4'b0011) ? op1&~op2 : adder_out;

endmodule
